using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Drawing;
using System.Globalization;
using System.IO;
using System.Text;
using System.Windows.Forms;
using ZoneFiveSoftware.Common.Data;
using ZoneFiveSoftware.Common.Data.Fitness;
using ZoneFiveSoftware.Common.Data.Measurement;
using ZoneFiveSoftware.Common.Visuals;
using ZoneFiveSoftware.Common.Visuals.Chart;
using ZoneFiveSoftware.Common.Visuals.Fitness;
using ZoneFiveSoftware.Common.Visuals.Util;
using CheckedComboBox;
using FilteredStatistics.Controller;
using FilteredStatistics.Data;
using FilteredStatistics.Common;
using FilteredStatistics.Common.Controller;

namespace FilteredStatistics.View
{
    partial class FilteredStatisticsControl : UserControl
    {
        public FilteredStatisticsControl(IDailyActivityView parentView)
        {
            InitializeComponent();

            PluginMain.GetApplication().PropertyChanged += new PropertyChangedEventHandler(OnApplicationPropertyChanged);
            PluginMain.LogbookChanged += new PluginMain.LogbookChangedEventHandler(OnLogbookChanged);

            m_ActivityCallbackHelper = new ActivityDataChangedHelper(null);

            Debug.Assert(parentView != null);
            m_ParentView = parentView;
            m_ParentView.SelectionProvider.SelectedItemsChanged += new EventHandler(OnParentViewSelectedItemsChanged);
            m_ParentView.RouteSelectionProvider.SelectedItemsChanged += new EventHandler(OnParentViewRouteSelectedItemsChanged);

            m_CurrentFilters = TemplateManager.Instance.LastUsedFilter;

            FilterListCtrl.FilterTypeColumn.Width = Options.Instance.CriteriaTypesColumnWidth;
            FilterListCtrl.ItemColumn.Width = Options.Instance.NamedZonesColumnWidth;

            Options.Instance.OptionsChanged += new Options.OptionsChangedEventHandler(OnOptionsChanged);
            Filters.FilterListChanged += new FilterList.FilterListChangedEventHandler(OnFilterListChanged);
            Filters.FilterChanged += new FilterList.FilterChangedEventHandler(OnFilterChanged);

            StatisticsChart.ChartFillColor = Options.Instance.ChartFillColor;
            StatisticsChart.ChartLineColor = Options.Instance.ChartLineColor;
            StatisticsChart.ChartSelectedColor = Options.Instance.ChartSelectedColor;
            StatisticsChart.ChartValidRegionColor = Options.Instance.ChartValidRegionColor;
            StatisticsChart.XAxisReferential = Options.Instance.ChartXAxis;
            StatisticsChart.YAxisReferential = Options.Instance.ChartYAxis;
            StatisticsChart.OverlayMode = Options.Instance.ChartOverlayMode;

            m_FilterResultsDistributor.RegisterStatisticsTreeList(StatisticsTreeList);

            // Load the settings and apply
            Options.Instance.StatisticsColumnsManager.LoadFromSettings();
            StatisticsController.Instance.RefreshTreeListColumns();
            RefreshVisibleStatisticsColumns();

            ChartActionBanner.Text = StatisticsChart.GetViewLabel();
            ChartActionBanner.SetMaximizable(ActivityDetailExtension.Instance.ActivityDetailPage);

            Enabled = false;
            Activity = CollectionUtils.GetSingleItemOfType<IActivity>(m_ParentView.SelectionProvider.SelectedItems);
        }

        void OnApplicationPropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (DetailPageVisible && e.PropertyName == "ActiveView")
            {
                if (PluginMain.GetApplication().ActiveView.Id == m_ParentView.Id)
                {
                    ShowResultsWindow(false);
                }
                else
                {
                    if (m_ResultsWindow != null)
                    {
                        HideResultsWindow();
                    }
                }
            }
        }

        private void OnLogbookChanged(object sender, ILogbook oldLogbook, ILogbook newLogbook)
        {
            TemplateManager.Instance.LastUsedFilter.Setup();
            m_CurrentFilters = TemplateManager.Instance.LastUsedFilter;
        }

        private void OnOptionsChanged(PropertyChangedEventArgs changedProperty)
        {
            // Refresh display colors
            StatisticsChart.ChartFillColor = Options.Instance.ChartFillColor;
            StatisticsChart.ChartLineColor = Options.Instance.ChartLineColor;
            StatisticsChart.ChartSelectedColor = Options.Instance.ChartSelectedColor;
            StatisticsChart.ChartValidRegionColor = Options.Instance.ChartValidRegionColor;

            // Refresh chart axes and label
            if (changedProperty.PropertyName == "ChartOverlayMode")
            {
                StatisticsChart.OverlayMode = Options.Instance.ChartOverlayMode;
            }
            else if (changedProperty.PropertyName == "ChartXAxis")
            {
                StatisticsChart.XAxisReferential = Options.Instance.ChartXAxis;
                RefreshChartFromSelection();
            }
            else if (changedProperty.PropertyName == "ChartYAxis")
            {
                StatisticsChart.YAxisReferential = Options.Instance.ChartYAxis;
                RefreshChartFromSelection();
            }
            else if (changedProperty.PropertyName == "StatisticsColumnManager.SortColumn")
            {
                m_FilterResultsDistributor.SetSortIndicator(Options.Instance.StatisticsColumnsManager.SortedColumnInfo.Id.ToString(),
                                                            Options.Instance.StatisticsColumnsManager.SortedColumnInfo.Sorting == TreeListColumnInfo.ColumnSortingType.Ascending);

                FillTreeList();
                SelectTotalsRowInList();
            }
            else if (changedProperty.PropertyName == "StatisticsColumnManager.DisplayedColumns" ||
                     changedProperty.PropertyName == "StatisticsColumnManager.ColumnWidth" ||
                     changedProperty.PropertyName == "StatisticsColumnManager.NumFixedColumns")
            {
                RefreshVisibleStatisticsColumns();
            }

            ChartActionBanner.Text = StatisticsChart.GetViewLabel();
        }

        private void OnFilterListChanged(FilterList filterList, PropertyChangedEventArgs changedProperty)
        {
            m_UpdateFilterResults = true;

            if (ActivityDetailExtension.Instance.ActivityDetailPage.Visible)
            {
                Invalidate();
            }
        }

        private void OnFilterChanged(Filter filter, PropertyChangedEventArgs changedProperty)
        {
            m_UpdateFilterResults = true;

            if (ActivityDetailExtension.Instance.ActivityDetailPage.Visible)
            {
                Invalidate();
            }
        }

        protected override void OnPaint(PaintEventArgs e)
        {
            if (m_UpdateFilterResults)
            {
                m_UpdateFilterResults = false;

                UpdateUIFromFilter(Filters);
                SelectTotalsRowInList();
            }

            base.OnPaint(e);
        }

        private void OnParentViewSelectedItemsChanged(object sender, EventArgs e)
        {
            Activity = CollectionUtils.GetSingleItemOfType<IActivity>(m_ParentView.SelectionProvider.SelectedItems);
        }

        private void OnParentViewRouteSelectedItemsChanged(object sender, EventArgs e)
        {
            if (Activity != null && Visible)
            {
                ValueRangeSeries<DateTime> selectedRanges = new ValueRangeSeries<DateTime>();

                if (m_ParentView.RouteSelectionProvider != null &&
                    m_ParentView.RouteSelectionProvider.SelectedItems != null)
                {
                    ActivityInfo activityInfo = ActivityInfoCache.Instance.GetInfo(Activity);

                    foreach (IItemTrackSelectionInfo selection in m_ParentView.RouteSelectionProvider.SelectedItems)
                    {
                        if (!(selection is FilteredStatisticsTrackSelectionInfo))
                        {
                            if (selection.MarkedDistances != null)
                            {
                                foreach (ValueRange<double> markedDistance in selection.MarkedDistances)
                                {
                                    ValueRange<DateTime> markedTime;

                                    markedTime = new ValueRange<DateTime>(activityInfo.ActualDistanceMetersTrack.GetTimeAtDistanceMeters(markedDistance.Lower),
                                                                          activityInfo.ActualDistanceMetersTrack.GetTimeAtDistanceMeters(markedDistance.Upper) + new TimeSpan(0, 0, 1));

                                    selectedRanges.Add(markedTime);
                                }
                            }

                            if (selection.MarkedTimes != null)
                            {
                                foreach (ValueRange<DateTime> markedTime in selection.MarkedTimes)
                                {
                                    selectedRanges.Add(markedTime);
                                }
                            }

                            if (selection.SelectedTime != null)
                            {
                                selectedRanges.Add(selection.SelectedTime);
                            }

                            if (selection.SelectedDistance != null)
                            {
                                ValueRange<DateTime> selectedTime;

                                selectedTime = new ValueRange<DateTime>(activityInfo.ActualDistanceMetersTrack.GetTimeAtDistanceMeters(selection.SelectedDistance.Lower),
                                                                        activityInfo.ActualDistanceMetersTrack.GetTimeAtDistanceMeters(selection.SelectedDistance.Upper) + new TimeSpan(0, 0, 1));

                                selectedRanges.Add(selectedTime);
                            }
                        }
                    }
                }

                if (selectedRanges.Count > 0)
                {
                    SelectedGPSActivityTimes = selectedRanges;
                }
            }
        }

#region UI callbacks
        private void FilteredStatisticsControl_Resize(object sender, EventArgs e)
        {
            ChartSplitContainer.SplitterDistance = Options.Instance.ChartSplitDistance;
            FiltersSplitContainer.SplitterDistance = Options.Instance.FiltersSplitDistance;
        }

        private void ChartSplitContainer_SplitterMoving(object sender, SplitterCancelEventArgs e)
        {
            Options.Instance.ChartSplitDistance = e.SplitY;
        }

        private void FiltersSplitContainer_SplitterMoving(object sender, SplitterCancelEventArgs e)
        {
            Options.Instance.FiltersSplitDistance = e.SplitY;
        }

        private void ChartActionBanner_MenuClicked(object sender, EventArgs e)
        {
            ContextMenu menu = new ContextMenu();
            MenuItem currentAxis;
            MenuItem menuItem;

            // Overlay mode
            MenuItem overlayMode = new IconizedSTMenuItem(PluginView.GetLocalizedString("OverlayModeText"),
                                                          new EventHandler(OverlayModeEventHandler));
            overlayMode.Checked = Options.Instance.ChartOverlayMode;
            menu.MenuItems.Add(overlayMode);

            // XAxis
            currentAxis = new IconizedSTMenuItem(PluginView.GetLocalizedString("XAxisText"));
            menu.MenuItems.Add(currentAxis);

            // Distance
            menuItem = new IconizedSTMenuItem(CommonResources.Text.LabelDistance,
                                              new EventHandler(DistanceMenuEventHandler));
            menuItem.Checked = (Options.Instance.ChartXAxis == ActivityDetailsLineChart.XAxisValue.Distance);
            currentAxis.MenuItems.Add(menuItem);

            // Time
            menuItem = new IconizedSTMenuItem(CommonResources.Text.LabelTime,
                                              new EventHandler(TimeMenuEventHandler));
            menuItem.Checked = (Options.Instance.ChartXAxis == ActivityDetailsLineChart.XAxisValue.Time);
            currentAxis.MenuItems.Add(menuItem);

            // YAxis
            currentAxis = new IconizedSTMenuItem(PluginView.GetLocalizedString("YAxisText"));
            menu.MenuItems.Add(currentAxis);

            foreach (Guid providerId in ActivityDataTrackController.Instance)
            {
                IActivityDataTrackProvider provider = ActivityDataTrackController.Instance.GetProvider(providerId);

                if (provider.IsPrimaryDataTrack)
                {
                    menuItem = new IconizedSTMenuItem(provider.YAxisLabel, new EventHandler(ProviderMenuEventHandler));
                    menuItem.Checked = (Options.Instance.ChartYAxis == provider.ReferenceId);
                    menuItem.Tag = provider;
                    currentAxis.MenuItems.Add(menuItem);
                }
            }

            menu.Show(this, PointToClient(MousePosition));
        }

        private void FilterListCtrl_ColumnWidthChanged(object sender, DataGridViewColumnEventArgs e)
        {
            if (e.Column == FilterListCtrl.FilterTypeColumn)
            {
                Options.Instance.CriteriaTypesColumnWidth = FilterListCtrl.FilterTypeColumn.Width;
            }
            else if (e.Column == FilterListCtrl.ItemColumn)
            {
                Options.Instance.NamedZonesColumnWidth = FilterListCtrl.ItemColumn.Width;
            }
        }

        private void SaveTemplateLinkLabel_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
        {
            SaveTemplateDialog dlg = new SaveTemplateDialog(Filters);

            if (Options.Instance.ShowSplitsTemplatesWarning)
            {
                // Check for "invalid" criterias (splits)
                foreach (Filter filter in Filters)
                {
                    if (filter.CriteriaFacade is ISplitsCriteria)
                    {
                        ISplitsCriteria concreteCriteria = filter.CriteriaFacade as ISplitsCriteria;

                        if (concreteCriteria.SplitsType == SplitsCriteriaType.RecordedLaps)
                        {
                            for(int i = 0; i < filter.GetSelectedZoneCount(); ++i)
                            {
                                INamedZone currentZone = filter.Zones[i];

                                // Not all active/rest criteria = invalid
                                if (filter.CriteriaFacade.NamedZones.IndexOf(currentZone) > 1)
                                {
                                    // Display warning that splits aren't saved in templates
                                    DialogResult questionAnswer = MessageBox.Show(PluginView.GetLocalizedString("SplitsTemplateWarningText"),
                                                                                  PluginView.GetLocalizedString("WarningText"),
                                                                                  MessageBoxButtons.YesNo,
                                                                                  MessageBoxIcon.Question);

                                    if (questionAnswer == DialogResult.No)
                                    {
                                        Options.Instance.ShowSplitsTemplatesWarning = false;
                                    }
                                }
                            }

                            break;
                        }
                    }
                }
            }

            if (dlg.ShowDialog() == DialogResult.OK)
            {
                if (dlg.SaveAsNew)
                {
                    Debug.Assert(TemplateManager.Instance.GetTemplate(dlg.TemplateName) == null);

                    Template newTemplate = new Template(dlg.TemplateName, Filters);

                    TemplateManager.Instance.AddTemplate(newTemplate);
                }
                else
                {
                    Template existingTemplate = TemplateManager.Instance.GetTemplate(dlg.TemplateName);

                    Debug.Assert(existingTemplate != null);

                    existingTemplate.Filters = Filters;
                }
            }
        }

        private void StatisticsTreeList_SelectedChanged(object sender, EventArgs e)
        {
            UpdateChartFromSelection();
            UpdateMapFromSelection();

            if (StatisticsTreeList.SelectedItems.Count > 0)
            {
                DeleteRangesButton.Enabled = StatisticsTreeList.SelectedItems.Count != 1 ||
                                             !(StatisticsTreeList.SelectedItems[0] is TotalStatsWrapper);
                MergeRangesButton.Enabled = AreAllSelectedRangesConsecutive();
            }
            else
            {
                DeleteRangesButton.Enabled = false;
                MergeRangesButton.Enabled = false;
            }

            m_FilterResultsDistributor.Selected = StatisticsTreeList.Selected;
        }

        private void StatisticsTreeList_ColumnResized(object sender, TreeList.ColumnEventArgs e)
        {
            Options.Instance.StatisticsColumnsManager.SetColumnWidth(new Guid(e.Column.Id), e.Column.Width);
        }

        private void StatisticsTreeList_ColumnClicked(object sender, TreeList.ColumnEventArgs e)
        {
            TreeListColumnInfo columnInfo = Options.Instance.StatisticsColumnsManager.FindColumn(new Guid(e.Column.Id));

            Debug.Assert(columnInfo != null);

            bool isAscending = columnInfo.Sorting == TreeListColumnInfo.ColumnSortingType.Ascending;

            if (isAscending)
            {
                Options.Instance.StatisticsColumnsManager.SetColumnSorting(new Guid(e.Column.Id), TreeListColumnInfo.ColumnSortingType.Descending);
            }
            else
            {
                Options.Instance.StatisticsColumnsManager.SetColumnSorting(new Guid(e.Column.Id), TreeListColumnInfo.ColumnSortingType.Ascending);
            }
        }

        private void StatisticsTreeList_MouseClick(object sender, MouseEventArgs e)
        {
            if (Activity != null && e.Button == MouseButtons.Right)
            {
                ContextMenu menu = new ContextMenu();
                IconizedSTMenuItem menuItem;

                menuItem = new IconizedSTMenuItem(PluginView.GetLocalizedString("ListSettingsText"),
                                                  new EventHandler(ListSettingsMenuEventHandler),
                                                  CommonResources.Images.Table16, null);
                menu.MenuItems.Add(menuItem);
                menuItem = new IconizedSTMenuItem(CommonResources.Text.ActionCopy,
                                                  new EventHandler(OnCopyResultsToClipboard));
                menu.MenuItems.Add(menuItem);
                menuItem = new IconizedSTMenuItem(CommonResources.Text.ActionExport,
                                                  new EventHandler(OnExportResultsToCSV),
                                                  CommonResources.Images.Export16, null);
                menu.MenuItems.Add(menuItem);

                menu.Show(this, PointToClient(MousePosition));
            }
        }

        private void StatisticsTreeList_KeyDown(object sender, KeyEventArgs e)
        {
            if (e.KeyCode == Keys.Delete)
            {
                DeleteSelectedRanges();
            }
            else if (e.KeyCode == Keys.M && e.Modifiers == Keys.Control)
            {
                MergeSelectedRanges();
            }
            else if (e.KeyCode == Keys.I && e.Modifiers == Keys.Control)
            {
                m_FilterResults.InvertResults(Activity);

                FillTreeList();
                SelectTotalsRowInList();
            }
        }

        private void StatisticsChart_ChartClick(object sender, double xValueClicked, double yValueClicked)
        {
            if (!Options.Instance.ChartOverlayMode)
            {
                if (StatisticsChart.IsInFillRegion(xValueClicked, yValueClicked))
                {
                    DateTime activityStartTime = Activity.StartTime;

                    // Find the range in which the x value fits
                    foreach (TreeList.TreeListNode node in StatisticsTreeList.RowData as List<TreeList.TreeListNode>)
                    {
                        // Ignore totals row
                        if (node is RangeWrapper)
                        {
                            RangeWrapper wrapper = node as RangeWrapper;
                            double rangeStartValue;
                            double rangeEndValue;

                            if (StatisticsChart.XAxisReferential == ActivityDetailsLineChart.XAxisValue.Time)
                            {
                                rangeStartValue = FilteredStatisticsStatisticsProvider.Instance.GetStartTimeValue(wrapper.InfoCache).TotalSeconds;
                                rangeEndValue = FilteredStatisticsStatisticsProvider.Instance.GetEndTimeValue(wrapper.InfoCache).TotalSeconds;
                            }
                            else
                            {
                                rangeStartValue = FilteredStatisticsStatisticsProvider.Instance.GetStartDistanceValue(wrapper.InfoCache);
                                rangeEndValue = FilteredStatisticsStatisticsProvider.Instance.GetEndDistanceValue(wrapper.InfoCache);
                            }

                            if (xValueClicked >= rangeStartValue && xValueClicked <= rangeEndValue)
                            {
                                HandleChartSelection(wrapper);
                                return;
                            }
                        }
                    }
                }

                m_FilterResultsDistributor.Selected = null;
            }
        }

        private void StatisticsChart_DataSerieClick(object sender, ChartDataSeries dataSerieClicked)
        {
            if (Options.Instance.ChartOverlayMode)
            {
                ValueRange<DateTime> selectedRange = StatisticsChart.GetRangeForDataSerie(dataSerieClicked);

                if (selectedRange != null)
                {
                    foreach (TreeList.TreeListNode node in StatisticsTreeList.RowData as List<TreeList.TreeListNode>)
                    {
                        // Ignore totals row
                        if (node is RangeWrapper)
                        {
                            RangeWrapper wrapper = node as RangeWrapper;

                            if (selectedRange.Equals(wrapper.InfoCache.PauselessRange))
                            {
                                HandleChartSelection(wrapper);
                                return;
                            }
                        }
                    }
                }

                m_FilterResultsDistributor.Selected = null;
            }
        }

        private void HandleChartSelection(RangeWrapper wrapper)
        {
            // Got it
            List<TreeList.TreeListNode> selection = new List<TreeList.TreeListNode>();

            if (ModifierKeys == Keys.Control)
            {
                // Copy existing selection
                foreach (TreeList.TreeListNode nodeToCopy in StatisticsTreeList.Selected)
                {
                    selection.Add(nodeToCopy);
                }

                // Remove selected clicked node
                if (selection.Contains(wrapper))
                {
                    selection.Remove(wrapper);
                }
                // Add newly selected clicked node
                else
                {
                    selection.Add(wrapper);
                }
            }
            else if (ModifierKeys == Keys.Shift && StatisticsChart.SelectionReference != null)
            {
                ValueRange<float> reference = StatisticsChart.SelectionReference;

                foreach (TreeList.TreeListNode nodeToCopy in StatisticsTreeList.RowData as List<TreeList.TreeListNode>)
                {
                    if (nodeToCopy is RangeWrapper)
                    {
                        RangeWrapper tempWrapper = nodeToCopy as RangeWrapper;
                        double tempRangeStartValue;
                        double tempRangeEndValue;
                        double rangeStartValue;
                        double rangeEndValue;

                        if (StatisticsChart.XAxisReferential == ActivityDetailsLineChart.XAxisValue.Time)
                        {
                            rangeStartValue = FilteredStatisticsStatisticsProvider.Instance.GetStartTimeValue(wrapper.InfoCache).TotalSeconds;
                            rangeEndValue = FilteredStatisticsStatisticsProvider.Instance.GetEndTimeValue(wrapper.InfoCache).TotalSeconds;
                            tempRangeStartValue = FilteredStatisticsStatisticsProvider.Instance.GetStartTimeValue(tempWrapper.InfoCache).TotalSeconds;
                            tempRangeEndValue = FilteredStatisticsStatisticsProvider.Instance.GetEndTimeValue(tempWrapper.InfoCache).TotalSeconds;
                        }
                        else
                        {
                            rangeStartValue = FilteredStatisticsStatisticsProvider.Instance.GetStartDistanceValue(wrapper.InfoCache);
                            rangeEndValue = FilteredStatisticsStatisticsProvider.Instance.GetEndDistanceValue(wrapper.InfoCache);
                            tempRangeStartValue = FilteredStatisticsStatisticsProvider.Instance.GetStartDistanceValue(tempWrapper.InfoCache);
                            tempRangeEndValue = FilteredStatisticsStatisticsProvider.Instance.GetEndDistanceValue(tempWrapper.InfoCache);
                        }

                        if (rangeEndValue < reference.Lower)
                        {
                            if (tempRangeStartValue >= rangeStartValue - Constants.Delta &&
                                tempRangeEndValue <= reference.Upper + Constants.Delta)
                            {
                                selection.Add(nodeToCopy);
                            }
                        }
                        else
                        {
                            if (tempRangeStartValue >= reference.Lower - Constants.Delta &&
                                tempRangeEndValue <= rangeEndValue + Constants.Delta)
                            {
                                selection.Add(nodeToCopy);
                            }
                        }
                    }
                }
            }
            else
            {
                selection.Add(wrapper);
            }

            m_FilterResultsDistributor.Selected = selection;
        }

        private void InvertResultsButton_Click(object sender, EventArgs e)
        {
			m_FilterResults.InvertResults(Activity);

			FillTreeList();
			SelectTotalsRowInList();
		}

        private void MergeRangesButton_Click(object sender, EventArgs e)
        {
            MergeSelectedRanges();
        }

        private void DeleteRangesButton_Click(object sender, EventArgs e)
        {
            DeleteSelectedRanges();
        }

        private void OpenInWindowButton_Click(object sender, EventArgs e)
        {
            ShowResultsWindow(true);
        }

        void OnResultsWindowFormClosed(object sender, FormClosedEventArgs e)
        {
            m_ResultsWindow.FormClosed -= new FormClosedEventHandler(OnResultsWindowFormClosed);
            m_ResultsWindow = null;
            m_ResultsWindowWasVisible = false;
        }

#endregion

        public void OverlayModeEventHandler(object sender, EventArgs args)
        {
            Options.Instance.ChartOverlayMode = !Options.Instance.ChartOverlayMode;
        }

        public void DistanceMenuEventHandler(object sender, EventArgs args)
        {
            Options.Instance.ChartXAxis = ActivityDetailsLineChart.XAxisValue.Distance;
        }

        public void TimeMenuEventHandler(object sender, EventArgs args)
        {
            Options.Instance.ChartXAxis = ActivityDetailsLineChart.XAxisValue.Time;
        }

        public void ProviderMenuEventHandler(object sender, EventArgs args)
        {
            MenuItem selectedMenuItem = sender as MenuItem;
            IActivityDataTrackProvider provider = selectedMenuItem.Tag as IActivityDataTrackProvider;

            Options.Instance.ChartYAxis = provider.ReferenceId;
        }

        public void ListSettingsMenuEventHandler(object sender, EventArgs args)
        {
            SelectColumnsForm dlg = new SelectColumnsForm(Options.Instance.StatisticsColumnsManager);

            if (dlg.ShowDialog() == DialogResult.OK)
            {
                Options.Instance.StatisticsColumnsManager.UpdateRegisteredColumns(dlg.VisibleColumns, dlg.HiddenColumns, dlg.FixedColumnCount);

                RefreshVisibleStatisticsColumns();
            }
        }

        private void OnCopyResultsToClipboard(object sender, EventArgs e)
        {
            StatisticsTreeList.CopyTextToClipboard(true, "\t");
        }

        private void OnExportResultsToCSV(object sender, EventArgs e)
        {
            SaveFileDialog dlg = new SaveFileDialog();
            string filter = PluginView.GetLocalizedString("CSVFileText") + " (*.csv)|*.csv";

            dlg.Filter = filter;

            if (dlg.ShowDialog() == DialogResult.OK)
            {
                Stream fileStream = dlg.OpenFile();

                StatisticsTreeList.ExportToCSV(fileStream);

                fileStream.Close();
            }
        }

        public void ThemeChanged(ITheme visualTheme)
        {
            StatisticsChart.ThemeChanged(visualTheme);
            ChartActionBanner.ThemeChanged(visualTheme);
            ChartPanel.ThemeChanged(visualTheme);
            FiltersPanel.ThemeChanged(visualTheme);
            m_FilterResultsDistributor.ThemeChanged(visualTheme);
            FilterListCtrl.ThemeChanged(visualTheme);

            ChartSplitContainer.Panel2.BackColor = visualTheme.Window;

            StatisticsController.Instance.RefreshTreeListColumns();
            RefreshVisibleStatisticsColumns();
        }

        public void RefreshVisibleStatisticsColumns()
        {
            List<TreeList.Column> columns = new List<TreeList.Column>();

            foreach (TreeListColumnInfo columnInfo in Options.Instance.StatisticsColumnsManager.VisibleColumns)
            {
                TreeList.Column newColumn = new TreeList.Column(columnInfo.Id.ToString(), columnInfo.HeaderText,
                                                                columnInfo.ColumnWidth, StringAlignment.Near);
                newColumn.CanSelect = true;

                columns.Add(newColumn);
            }

            m_FilterResultsDistributor.Columns = columns;
            m_FilterResultsDistributor.NumLockedColumns = Options.Instance.StatisticsColumnsManager.FixedColumnCount;

            TreeListColumnInfo sortedColumn = Options.Instance.StatisticsColumnsManager.SortedColumnInfo;
            if (sortedColumn != null)
            {
                m_FilterResultsDistributor.SetSortIndicator(sortedColumn.Id.ToString(), sortedColumn.Sorting == TreeListColumnInfo.ColumnSortingType.Ascending);
            }
        }

        public void UICultureChanged(CultureInfo culture)
        {
            StatisticsChart.UICultureChanged(culture);
            FilterListCtrl.UICultureChanged(culture);

            ChartActionBanner.Text = StatisticsChart.GetViewLabel();
            SaveTemplateLinkLabel.Text = PluginView.GetLocalizedString("SaveTemplateButtonText");

            StatisticsController.Instance.RefreshTreeListColumns();
            RefreshVisibleStatisticsColumns();
        }

        private void UpdateUIFromFilter(FilterList filterList)
        {
            FilterListCtrl.Filters = filterList;

            RefreshFilterResults(filterList);
            UpdateSaveAsTemplateControl(filterList);
        }

        private void RefreshFilterResults(FilterList filterList)
        {
            m_FilterResults.Clear();
			m_FilterResults.AddRange(filterList.GetMatchingRanges());
            m_FilterResults = PostProcessFilterController.ApplyPostProcessFilters(Filters, m_FilterResults, Activity);

            if (m_FilterResults != null && Activity != null)
            {
                FillTreeList();
            }
            else
            {
                m_FilterResultsDistributor.RowData = null;
            }
        }

        private void FillTreeList()
        {
            List<ActivityRangeInfoCache> rangeWrappers = new List<ActivityRangeInfoCache>();
            List<TreeList.TreeListNode> filterResultsNodes = new List<TreeList.TreeListNode>();

            foreach (ValueRange<DateTime> currentRange in m_FilterResults)
            {
                rangeWrappers.Add(new ActivityRangeInfoCache(currentRange, Activity));
            }

            if (rangeWrappers.Count > 0)
            {
                if (Options.Instance.StatisticsColumnsManager.SortedColumnInfo != null)
                {
                    rangeWrappers.Sort(new RangeWrapperColumnComparer(Options.Instance.StatisticsColumnsManager.SortedColumnInfo));
                }

                foreach (ActivityRangeInfoCache range in rangeWrappers)
                {
                    filterResultsNodes.Add(new RangeWrapper(range));
                }

                // Add totals row
                filterResultsNodes.Add(new TotalStatsWrapper(rangeWrappers, Activity));
            }

            m_FilterResultsDistributor.RowData = filterResultsNodes;

			UpdateChartValidRanges();
        }

        private void SelectTotalsRowInList()
        {
            List<TreeList.TreeListNode> rows = StatisticsTreeList.RowData as List<TreeList.TreeListNode>;
            List<TreeList.TreeListNode> selection = new List<TreeList.TreeListNode>();

            if (rows != null && rows.Count > 0)
            {
                selection.Add(rows[rows.Count - 1]);
            }

            m_FilterResultsDistributor.Selected = selection;
        }

        private void RefreshChartFromSelection()
        {
            StatisticsChart.RefreshSelection();
        }

		private void UpdateChartValidRanges()
		{
			StatisticsChart.PaintActive = false;
			{
				StatisticsChart.ClearValidRanges();

				if (Activity != null)
				{
					// Add all the valid ranges first
					foreach (TreeList.TreeListNode node in StatisticsTreeList.RowData as List<TreeList.TreeListNode>)
					{
						if (node is RangeWrapper)
						{
							RangeWrapper wrapper = node as RangeWrapper;

                            StatisticsChart.AddValidRange(wrapper.InfoCache.PauselessRange);
						}
					}
				}
			}
			StatisticsChart.PaintActive = true;
		}

        private void UpdateChartFromSelection()
        {
            StatisticsChart.PaintActive = false;
            {
                StatisticsChart.ClearSelectedRange();

                if (Activity != null)
                {
                    // Get the selected ranges
                    foreach (TreeList.TreeListNode node in StatisticsTreeList.SelectedItems)
                    {
                        if (node is RangeWrapper)
                        {
                            RangeWrapper wrapper = node as RangeWrapper;

                            StatisticsChart.AddSelectedRange(wrapper.InfoCache.PauselessRange);
                        }
                        else if (node is TotalStatsWrapper)
                        {
                            TotalStatsWrapper wrapper = node as TotalStatsWrapper;

                            foreach (ActivityRangeInfoCache range in wrapper.AllRanges)
                            {
                                StatisticsChart.AddSelectedRange(range.PauselessRange);
                            }
                        }
                    }
                }
            }
            StatisticsChart.PaintActive = true;
        }

        public void UpdateMapFromSelection()
        {
            if (m_Activity != null && m_Activity.GPSRoute != null)
            {
                ValueRangeSeries<DateTime> selectedRanges = new ValueRangeSeries<DateTime>();

                if (DetailPageVisible)
                {
                    foreach (TreeList.TreeListNode node in StatisticsTreeList.SelectedItems)
                    {
                        if (node is RangeWrapper)
                        {
                            RangeWrapper wrapper = node as RangeWrapper;

                            selectedRanges.Add(wrapper.InfoCache.RangeWithPauses);
                        }
                        else if (node is TotalStatsWrapper)
                        {
                            TotalStatsWrapper wrapper = node as TotalStatsWrapper;

                            selectedRanges.Clear();
                            foreach (ActivityRangeInfoCache range in wrapper.AllRanges)
                            {
                                selectedRanges.Add(range.RangeWithPauses);
                            }

                            break;
                        }
                    }
                }

                FilteredStatisticsTrackSelectionInfo mapSelection = new FilteredStatisticsTrackSelectionInfo(m_Activity);

                mapSelection.MarkedTimes = selectedRanges;
                m_ParentView.RouteSelectionProvider.SelectedItems = new IItemTrackSelectionInfo[] { mapSelection };
            }
        }

        private void UpdateSaveAsTemplateControl(FilterList filter)
        {
            SaveTemplateLinkLabel.Enabled = filter.GetFilterCount() > 1;
        }

        private bool AreAllSelectedRangesConsecutive()
        {
            List<ValueRange<DateTime>> rangesToMerge = new List<ValueRange<DateTime>>();

			foreach (TreeList.TreeListNode node in StatisticsTreeList.SelectedItems)
			{
				if (node is RangeWrapper)
				{
					RangeWrapper wrapper = node as RangeWrapper;

                    rangesToMerge.Add(wrapper.InfoCache.RangeWithPauses);
				}
				else if (node is TotalStatsWrapper)
				{
					// Don't allow totals row in merge
					return false;
				}
			}

            rangesToMerge.Sort(Utils.CompareDateTimeRangeAscending);

			return m_FilterResults.RangesAreConsecutive(rangesToMerge);
        }

        private void MergeSelectedRanges()
        {
            bool itemsMerged = false;
            List<ValueRange<DateTime>> rangesToMerge = new List<ValueRange<DateTime>>();

            foreach (TreeList.TreeListNode node in StatisticsTreeList.SelectedItems)
            {
                if (node is RangeWrapper)
                {
                    RangeWrapper wrapper = node as RangeWrapper;

                    rangesToMerge.Add(wrapper.InfoCache.RangeWithPauses);
                }
            }

            rangesToMerge.Sort(Utils.CompareDateTimeRangeAscending);

            itemsMerged = m_FilterResults.MergeResults(rangesToMerge);

            if (itemsMerged)
            {
                FillTreeList();
                SelectTotalsRowInList();
            }
        }

        private void DeleteSelectedRanges()
        {
            bool itemDeleted = false;

            foreach (TreeList.TreeListNode node in StatisticsTreeList.SelectedItems)
            {
                if (node is RangeWrapper)
                {
                    RangeWrapper wrapper = node as RangeWrapper;

                    m_FilterResults.Remove(wrapper.InfoCache.RangeWithPauses);
                    itemDeleted = true;
                }
            }

            if (itemDeleted)
            {
                FillTreeList();
                SelectTotalsRowInList();
            }
        }

        public void ShowResultsWindow(bool always)
        {
            if (always || m_ResultsWindowWasVisible)
            {
                if (m_ResultsWindow == null)
                {
                    m_ResultsWindow = new FilterResultsForm(m_FilterResultsDistributor);
                    m_ResultsWindow.FormClosed += new FormClosedEventHandler(OnResultsWindowFormClosed);

                    FillTreeList();
                    RefreshVisibleStatisticsColumns();

                    m_ResultsWindow.Show(this);
                }

                m_ResultsWindow.Focus();
            }
        }

        public void HideResultsWindow()
        {
            if(m_ResultsWindow != null)
            {
                m_ResultsWindow.Close();
                m_ResultsWindowWasVisible = true;
            }
            else
            {
                m_ResultsWindowWasVisible = false;
            }
        }

        private IActivity Activity
        {
            get { return m_Activity; }
            set
            {
                if (m_Activity != value)
                {
                    m_Activity = value;

                    m_ActivityCallbackHelper.Activity = Activity;
                    FilterCriteriaController.Instance.Activity = Activity;
                    Filters.Activity = Activity;
                    StatisticsChart.Activity = Activity;

                    // TODO, find a place to handle singleton's activity setting
                    ActivityDataTrackController.Instance.Activity = Activity;
                    CustomizableCriteriasManager.Instance.Activity = Activity;
                    WholeActivityFilteredDataSet.Instance.Activity = Activity;
                    Options.Instance.StatisticsColumnsManager.Activity = Activity;
                    
                    if(m_ResultsWindow != null)
                    {
                        m_ResultsWindow.Activity = Activity;
                    }

                    ChartActionBanner.Text = StatisticsChart.GetViewLabel();

                    StatisticsController.Instance.RefreshTreeListColumns();
                    RefreshVisibleStatisticsColumns();

                    // Enable/Disable UI
                    Enabled = (Activity != null);

                    if (Activity == null)
                    {
                        m_FilterResultsDistributor.RowData = null;
                    }
                    else
                    {
                        UpdateUIFromFilter(Filters);

                        // Select all when changing the filters
                        SelectTotalsRowInList();
                    }
                }
            }
        }

        private FilterList Filters
        {
            get { return m_CurrentFilters; }
        }

        public ValueRangeSeries<DateTime> SelectedGPSActivityTimes
        {
            set
            {
                List<TreeList.TreeListNode> selection = new List<TreeList.TreeListNode>();

                if (Activity != null && value != null)
                {
                    foreach (IValueRange<DateTime> selectedRange in value)
                    {
                        // Go through all valid ranges and select the one which includes the distance
                        foreach (TreeList.TreeListNode node in StatisticsTreeList.RowData as List<TreeList.TreeListNode>)
                        {
                            if (node is RangeWrapper)
                            {
                                RangeWrapper wrapper = node as RangeWrapper;

                                // Validate that ranges intersect
                                if (!((selectedRange.Lower < wrapper.InfoCache.RangeWithPauses.Lower &&
                                      selectedRange.Upper < wrapper.InfoCache.RangeWithPauses.Lower) ||
                                     (selectedRange.Lower > wrapper.InfoCache.RangeWithPauses.Upper &&
                                      selectedRange.Upper > wrapper.InfoCache.RangeWithPauses.Upper)) &&
                                    !selection.Contains(node))
                                {
                                    selection.Add(node);
                                }
                            }
                        }
                    }
                }

                m_FilterResultsDistributor.Selected = null;
                m_FilterResultsDistributor.Selected = selection;
            }
        }

        public bool DetailPageVisible
        {
            get { return m_DetailPageVisible; }
            set
            {
                if(DetailPageVisible != value)
                {
                    m_DetailPageVisible = value;

                    if (DetailPageVisible)
                    {
                        ShowResultsWindow(false);
                    }
                    else
                    {
                        HideResultsWindow();
                    }

                    UpdateMapFromSelection();
                }
            }
        }

        private IActivity m_Activity = null;
        private ActivityDataChangedHelper m_ActivityCallbackHelper = null;
        private IDailyActivityView m_ParentView = null;
        private FilterListWithCache m_CurrentFilters = null;
		private FilterResults m_FilterResults = new FilterResults();
        private FilterResultsForm m_ResultsWindow = null;
        private StatisticsTreeListDistributor m_FilterResultsDistributor = new StatisticsTreeListDistributor();
        private bool m_UpdateFilterResults = false;
        private bool m_DetailPageVisible = false;
        private bool m_ResultsWindowWasVisible = false;
   }
}
